//==============================================================================
// Copyright (c) 2008-2013 Niall Ryan. All Rights Reserved.
//==============================================================================

#ifndef HYDRA_LOCKS_FILTERLOCK_H
#define HYDRA_LOCKS_FILTERLOCK_H 1

namespace Hydra
{
	template<unsigned int N>
	class FilterLock
	{
	public:
		FilterLock()
		{
			for( unsigned int i=0; i<N; ++i )
				m_level[i] = 0;
		}
		void lock(unsigned int id)
		{
			assert(id < N);
			for( unsigned int i=1; i<N; ++i )
			{
				m_level[id] = i;
				m_victim[i].store<FullFence>(id);	//full fence is required here to prevent subsequent reads moving before
													// this store, standard acquire/release semantics are insufficient
				//loop until we have exclusive access to level i
				unsigned int numClear = 0;
				unsigned int testId = 0;
				Backoff backoff;
				while( numClear < N-1 )
				{
					if( testId != id )
					{
						if( (m_level[testId] >= i) && (m_victim[i] == id) )
						{
							numClear = 0;
							backoff.wait();
						}
						else
							++numClear;
					}

					++testId;
					if( testId >= N )
						testId = 0;
				}
			}
		}
		void unlock(unsigned int id)
		{
			assert(id < N);
			m_level[id] = 0;
		}
	private:
		Atomic<unsigned int> m_level[N];
		Atomic<unsigned int> m_victim[N];
	};
}

#endif
